function set_checked(arr1,arr2)
{
	if (!arr2) return;
	var h={};
	for (var i=0,s=arr2.length;i<s;i++) h[arr2[i]]=1;
	for (var i=0,s=arr1.length;i<s;i++) if (h[arr1[i].id]) arr1[i].checked=1;
}

exports.add=[{
	_type:"controller",
	_config_details: {
		
	},
	_config: {
		name: "Market",
		action: "/market/",
		category_views: ["category","category__item","category__subcat"],
		selection: null,
		market_items_selection:null,
		nav_root_dropdown: 1
		/*
		filters={ // soft filters
			limit
			sort
		}

		*/
	},
	/**
	 * Controller: Market.index
	 *
	 */
	index: function(config) {
		return this.Cview();
	},

	/**
	 *
	 */
	paginator: function(config) {
		var global;
		if (this.market_global_search) global=this.market_global_search;
		if (this.market_category && this.market_category.items) global=this.market_category;
		this.market_paginator=undefined;
		if (!global) return this.Cview();
		if (global.items.length==global.items_count) return this.Cview();
		if (!global.items_config || !global.items_config.limit) return this.Cview();
		var href=this.F("Market","gen_same_filters_url",global.items_config,global.items_filters);
		href=href.replace(/[?&]page=\d+/,"");
		if (href.match(/\?./)) href+="&page="; else href+="?page=";

		this.market_paginator=this.F("Functions","gen_page_numbers",Math.round(global.items_config.offset/global.items_config.limit),Math.ceil(global.items_count/global.items_config.limit));
		this.market_paginator.forEach(function(p) {
			p.href=href+p.i;
		});
		if (this.market_paginator.length) this.market_paginator.push({page:this.F("Views","L","controllers/Market","All pages",2),href:href+"all",i:"all"});
		return this.Cview();
	},

	/**
	 * Controller: Market.nav_path_helper
	 */
	nav_path_helper: function(config) {
		var t=this;
		if (!(this.pageinfo.prerun.match(/^Market/))) return;
		var np=config.nav_path;
		if (np.length) np[np.length-1].clickable=1;

		function rec_add_cat(cat,l) {
			if (!l) l=0;
			if (cat.parent_id) rec_add_cat(t.site.models.MarketCategory.Get(cat.parent_id),l+1);
			np.push({url:config.action+"category/"+cat.code+"/",name:cat.name,clickable:l});
		}

		if (this.market_item) {
			//np.push({url:this.alias+this.article.calc_code+"/",name:this.article.name,clickable:false});
			rec_add_cat(this.site.models.MarketCategory.Get(this.market_item.category_id),1);
			np.push({url:this.alias+"category/"+this.market_item.code+"/",name:this.market_item.name,clickable:false});
		} else if (this.market_category) {
			rec_add_cat(this.market_category);
//			np.push({url:this.alias+"category/"+this.market_category.code+"/",name:this.market_category.name,clickable:false});
		} else if (this.market_manufacturer) {
			np.push({url:this.alias+"manufacturer/"+this.market_manufacturer.code+"/",name:this.market_manufacturer.name,clickable:false});
		}
	},
	
	/**
	 * Controller: Market.categories_nav_root_helper
	 */
	categories_nav_root_helper: function(config) {
		this.configMarket=config;
		this.market_categories_root=this.site.models.MarketCategory.List("list_level_enabled");
		if (!this.calc_pages) this.F("Nav","calc");
		if (!this.calc_pages.active_market_categories) this.F("Market","calc_categories");
		for (var i=0;i<this.market_categories_root.length;i++) {
			var mc=this.market_categories_root[i];
			if (this.calc_pages.active_market_categories[mc.id]) mc.active=1;
			if (config.nav_root_dropdown) {
				var c=this.site.models.MarketCategory.List("list_level_enabled",{parent_id:mc.id});
				if (c.length) mc.children=c;
			}
		}
		return this.Cview();
	},

	/**
	 * Controller: Market.global_search
	 */
	global_search: function(config) {
		this.configMarket=config;

		
		//throw config;
		
		var filters=this.F("Market","gen_filters");
		var conf=this.F("Market","gen_conf",config);
		
		//throw {config:config,conf:conf,filters:filters}
		this.market_global_search={
			items:this.F("Market","select_items",config,conf,filters),
			items_count:this.F("Market","count_items",config,conf,filters),
			items_config:conf,
			items_filters:filters
		};
		//throw this.market_global_search.items;
		if (this.fields.ajaj) {
			return JSON.stringify({call:"MarketFuncs.filters_done",html:this.Cview()});
		}

		return this.Cview();
	},

	/**
	 * Controller: Market.filters
	 * Parameters:
	 *		config.filters_helpers typeof Array	- Array of Contoller mode names to be called just before generating HTML, defaults to null
	 */
	filters: function(config) {
		this.configMarket=config;
		var httpfilters=this.F("Market","gen_filters");
		this.market_filters={
			name_like:httpfilters.name_like,
			fid:httpfilters.fid
		};
		var t=this;
		var mc=this.market_category;
		if (!mc) {
			var cc=this.fields.category_code||config.category_code;
			mc=this.site.models.MarketCategory.List("get_by_code",{code:cc})[0];
			if (!mc) mc={};
		}
//		this.market_filters=[];
		var c1=t.site.models.MarketFieldGroup.List("list_of_empty_category",{category_id:mc.id});
		var c2=[];
		if (cc) c2=t.site.models.MarketFieldGroup.List("list_of_category",{category_id:mc.id});

		this.market_filters_html="";
		this.market_filters_category_id=mc.id;
		var dts={};

		var all_fields=[];
		c1.concat(c2).forEach(function(grp) {
			//t.market_filters=t.market_filters.concat(t.site.models.MarketField.List("list_of_group_and_search_level_between",{group_id:grp.id,search_level_min_code:"basic",search_level_max_code:config.search_level_max_code||"basic"}));
			//throw t.site.models.MarketField.List("list_of_group_and_search_level_between",{group_id:grp.id,search_level_min_code:"basic",search_level_max_code:config.search_level_max_code||"basic"});
			t.site.models.MarketField.List("list_of_group_and_search_level_between",{group_id:grp.id,search_level_min_code:"basic",search_level_max_code:config.search_level_max_code||"basic"}).forEach(function(f) {
				f.col=(grp.category_id?"fldcat":"fldany");
				all_fields.push(f);
			});
		});
//		throw all_fields;
		if (config.filters_helpers) {
			config.filters_helpers.forEach(function(fh) {
				var arr=fh.match(/^(\w+)\.(\w+)$/);
				var r=t.C(arr[1],arr[2],{all_fields:all_fields});
				if (r) throw r;
			});
		}
		//throw all_fields;
		all_fields.forEach(function(fld) {
			if (!dts[fld.datatype_id]) dts[fld.datatype_id]=t.site.models.MarketFieldDatatype.Get(fld.datatype_id);
			var dt=dts[fld.datatype_id];
			t.temp = dt;
			//throw {fld:fld,dt:dt};
			if (!dt.view_code) return;
			t.f=fld;
			var table="Tmarket_item_fields_"+dt.prefix;
			var col=fld.col+fld.cid;
			switch (dt.code+(fld.is_custom?"--":"")) {
				case "integer":
				case "float":
					var minmax=t.site.sql.execute_and_fetch_one_single("select min("+col+") as minval,max("+col+") as maxval from "+table+" t,Tmarket_items mi where t.item_id=mi.id and mi.enabled=1"+(mc.id?" and mi.category_id=:category_id":""),{category_id:mc.id});
					t.f.min=minmax.minval;
					t.f.max=minmax.maxval;
					if (t.f.min==t.f.max) return;
					t.f.cmin=httpfilters.ranges[t.f.id]?httpfilters.ranges[t.f.id].min:t.f.min;
					t.f.cmax=httpfilters.ranges[t.f.id]?httpfilters.ranges[t.f.id].max:t.f.max;
					break;
				case "enum":
				case "lenum":
					t.f.values=[];
					var enums=t.site.sql.execute_and_fetch_single("select "+col+" as id from "+table+" t,Tmarket_items mi where t.item_id=mi.id and mi.enabled=1"+(mc.id?" and mi.category_id=:category_id":"")+" group by "+col,{category_id:mc.id});
					var ffff=t.site.models[dt.code=="enum"?"MarketFieldEnum":"MarketFieldEnuml"];
					enums.forEach(function(e) {
						if (!e.id) return;
						var tmp=ffff.Get(e.id);
						if (!tmp) throw new Error((dt.code=="enum"?"MarketFieldEnum":"MarketFieldEnuml")+" id="+e.id+" not found!");
						t.f.values.push(tmp);
					});
					//throw col;//t.f.values;
					if (t.f.values.length==0) return;
					t.f.values.sort(function(a,b) { if (a.value<b.value) return -1; if (a.value>b.value) return 1; else return 0;});
					set_checked(t.f.values,httpfilters.enums[t.f.id]);
					break;
				case "lenums":
					throw "TODO";
				case "enums":
					t.f.values=t.site.sql.execute_and_fetch_single("select mfe.id,mfe.value from Tmarket_field_enums mfe where mfe.field_id=:field_id and exists (select 1 from Tmarket_item_fields_enums mife where mife.enum_id=mfe.id) order by mfe.value",{field_id:fld.id});
					if (t.f.values.length==0) return;
					set_checked(t.f.values,httpfilters.enums[t.f.id]);
					break;
			}
			t.market_filters_html+=t.View("controllers/Market/filters__"+dt.view_code);
		});
		if (config.filter_manufacturers) {
			if (mc.id) {
				this.market_filters.manufacturers=this.site.models.MarketManufacturer.List("list_of_category_enabled",{category_id:mc.id});
			} else {
				this.market_filters.manufacturers=this.site.models.MarketManufacturer.List("list_enabled_having_items");
			}
			set_checked(this.market_filters.manufacturers,httpfilters.manufacturers);
		}
		if (config.filter_prices) {
			this.market_filters.prices=t.site.sql.execute_and_fetch_one_single("select min(price) as minval,max(price) as maxval from Tmarket_items mi where mi.price is not null and mi.enabled=1"+(mc.id?" and mi.category_id=:category_id":""),{category_id:mc.id});
			this.market_filters.prices.cminval=httpfilters.price?httpfilters.price.min:this.market_filters.prices.minval;
			this.market_filters.prices.cmaxval=httpfilters.price?httpfilters.price.max:this.market_filters.prices.maxval;
		}
		return this.Cview();
	},


	/**
	 * Controller: Market.manufacturers_list
	 *
	 * HTTP args:
	 *
	 * View variables:
	 *		this.market_manufacturers
	 */
	manufacturers_list: function(config) {
		this.configMarket=config;
		this.market_manufacturers=this.site.models.MarketManufacturer.List(config.market_items_selection||"list_enabled");
		return this.Cview();
	},

	/**
	 * Controller: Market.manufacturer
	 *
	 * HTTP args:
	 *		manufacturer_code
	 *
	 * View variables:
	 *		this.market_manufacturer
	 */
	manufacturer: function(config) {
		this.configMarket=config;
		var a=this.market_manufacturer=this.site.models.MarketManufacturer.List("get_by_code",{code:this.fields.manufacturer_code})[0];
		if (!this.market_manufacturer) return "No manufacturer found";

		if (a.title) {
			if (a.title) this.pageinfo.title=(config.meta_replace)?a.title:a.title+" - "+this.pageinfo.title;
		} else {
			if (a.name) this.pageinfo.title=(config.meta_replace)?a.name:a.name+" - "+this.pageinfo.title;
		}
		if (a.meta_abstract) this.pageinfo.meta_abstract=(config.meta_replace)?a.meta_abstract:a.meta_abstract+" - "+this.pageinfo.meta_abstract;
		if (a.meta_description) this.pageinfo.meta_description=(config.meta_replace)?a.meta_description:a.meta_description+" - "+this.pageinfo.meta_description;


		return this.Cview();
	},

	/**
	 * Controller: Market.root_categories
	 *
	 * HTTP args:
	 *
	 * View variables:
	 *		this.market_categories		typeof Array of MarketCategory model
	 */
	root_categories: function(config) {
		this.configMarket=config;
		this.market_root_categories=this.site.models.MarketCategory.List("list_level_enabled",{parent_id:null});
		return this.Cview();
	},
	
	/**
	 * Controller: Market.categories_tree
	 *
	 * HTTP args:
	 *
	 * View variables:
	 *		this.market_categories_tree		typeof Array of MarketCategory model
	 */
	categories_tree: function(config) {
		var t=this;
		this.configMarket=config;
		this.market_categories_tree=this.site.models.MarketCategory.ListHier("list_level_enabled");
		this.market_categories_tree.forEach(function(cat) {
			if (cat.code==t.fields.category_code) cat.active=1;
		});
		return this.Cview();
	},

	/**
	 * Controller: Market.category
	 *
	 * HTTP args:
	 *		category_code
	 *
	 * View variables:
	 *		this.market_category			typeof MarketCategory model
	 *		this.market_category.subcats	typeof Array of MarketCategory model
	 *		this.market_category.items		typeof Array of MarketItem model
	 */
	category: function(config) {
		this.configMarket=config;
		var cc=this.fields.category_code||config.category_code;
		var mc=this.market_category=this.site.models.MarketCategory.List("get_by_code",{code:cc})[0];
		if (!this.market_category) return "No category found";
		if (this.fields.only_items) {
			this.market_category.subcats=[];
		} else {
			this.market_category.subcats=this.site.models.MarketCategory.List("list_level_enabled",{parent_id:mc.id});
		}

		var filters=this.F("Market","gen_filters");
		var conf=this.F("Market","gen_conf",config);
		filters.category_id=mc.id;
		this.market_category.items=this.F("Market","select_items",config,conf,filters);
		this.market_category.items_count=this.F("Market","count_items",config,conf,filters);
		this.market_category.items_config=conf;
		this.market_category.items_filters=filters;

//		this.pageinfo.title_override=
		if (this.fields.ajaj) {
			return JSON.stringify({call:"MarketFuncs.filters_done",html:this.Cview()});
		}
		return this.Cview();
	},

	/**
	 * Controller: Market.category_with_hier_items
	 */
	category_with_hier_items: function(config) {
		var t=this;
		var filters=this.F("Market","gen_filters");
		var conf=this.F("Market","gen_conf",config);
		this.configMarket=config;
		var cc=config.strong_category_code||this.fields.category_code||config.category_code;
		var mc=this.market_category=this.site.models.MarketCategory.List("get_by_code",{code:cc})[0];
		if (!this.market_category) return "No category found";
		if (mc.parent_id) mc.parent=this.site.models.MarketCategory.Get(mc.parent_id);
		filters.category_id=mc.id;
		this.market_category.items_groupped=[
			{
				category:{},
				items:this.F("Market","select_items",config,conf,filters),
				items_count:this.F("Market","count_items",config,conf,filters)
			}
		];
		this.site.models.MarketCategory.ListHier("list_level_enabled",{parent_id:mc.id}).forEach(function(mc2) {
			filters.category_id=mc2.id;
			t.market_category.items_groupped.push(
				{
					category:mc2,
					items:t.F("Market","select_items",config,conf,filters),
					items_count:t.F("Market","count_items",config,conf,filters)
				}
			);
		});
		
		return this.Cview();
	},

	/**
	 * Controller: Market.category_siblings
	 *
	 * HTTP args:
	 *		category_code
	 *
	 * View variables:
	 */
	category_siblings: function(config) {
		var t=this;
		this.configMarket=config || {};
		var cc=this.configMarket.forced_category_code||this.fields.category_code||config.category_code;
		var mc=this.site.models.MarketCategory.List("get_by_code",{code:cc})[0];
		if (!mc) return "No category found";
		this.market_category_siblings=this.site.models.MarketCategory.List("list_level_enabled",{parent_id:mc.parent_id});
		this.market_category_siblings.forEach(function(c) {
			if (c.id==mc.id) c.active=1;
			if (t.configMarket.with_childs) c.childs = t.site.models.MarketCategory.List("list_level_enabled",{parent_id:c.id});
		});
		this.mc = mc;
		return this.Cview();
	},

	/**
	 * Controller: Market.manufacturer_categories_list
	 */
	manufacturer_categories_list: function(config) {
		var t=this;
		this.configMarket=config;
		this.market_manufacturer_categories=this.site.models.MarketCategory.List("list_of_manufacturer_enabled",{manufacturer_id:this.fields.manufacturer_id||config.manufacturer_id});
		return this.Cview();
	},

	/**
	 * Controller: Market.some_items_list
	 *
	 * Parameters:
	 *		config.filters typeof MarketFilters
	 *
	 * HTTP args:
	 *
	 * View variables:
	 *		this.market_some_items			typeof Array of MarketItem model
	 */
	some_items_list: function(config) {
		var t=this;
		this.configMarket=config;
//		this.market_some_items=this.site.models.MarketItem.List(config.selection);
//		if (config.limit) this.market_some_items.splice(config.limit,this.market_some_items.length-config.limit);
		this.market_some_items=this.F("Market","select_items",config,config.filters,config.filters);
		return this.Cview();

	},

	/**
	 * Controller: Market.item
	 *
	 * HTTP args:
	 *		item_code
	 *
	 * View variables:
	 *		this.market_item				typeof MarketItem model
	 *		this.market_item.file_folder	typeof Array
	 *		this.market_category			typeof MarketCategory model
	 *		this.market_manufacturer		typeof MarketManufacturer model
	 */
	item: function(config) {
		this.configMarket=config;
		this.market_item=this.site.models.MarketItem.List("get_by_code",{code:this.fields.item_code})[0];
		var err;
		if (!this.market_item) {
			err="No item found";
		} else if (!this.market_item.enabled) {
			err="Item no longer in stock";
		}
		if (err) {
			this.err=err;
			this.http_referer=system.env.HTTP_REFERER;
			this.remote_addr=system.env.REMOTE_ADDR;
			this.http_user_agent=system.env.HTTP_USER_AGENT;
			if (!this.http_user_agent.match(/(Googlebot|YandexBot)/i)) this.F("Functions","send_mail","market_no_item",{});
			this.jsng_response.status_code="404";
			return err;
		}
		this.market_category=this.site.models.MarketCategory.Get(this.market_item.category_id);
		this.market_manufacturer=this.site.models.MarketManufacturer.Get(this.market_item.category_id);
		this.market_item.FetchFileFolderFiles();
		if (this.market_item.file_folder_id) this.market_item.file_folder_files = this.site.models.File.List('list_of_file_folder',{file_folder_id:this.market_item.file_folder_id});//GD
		this.market_item.cart_amount=0;
		if (!this.cart) this.cart=this.F("Cart","get_user_fill");
		this.cart.FetchItemsFull();
		this.market_item.cart_amount=this.cart.GetAmountOf({market_item_id:this.market_item.id});
		if (config.item_title) this.pageinfo.title=this.views.apply(config.item_title,this);
		return this.Cview();
	},

	/**
	 * Controller: Market.item_plus
	 *
	 * Parameters:
	 *		config.item_plus.same typeof String			- "fid" or "fid,group"
	 *		this.fields.code
	 *
	 * View variables:
	 *		this.market_item				typeof MarketItem model
	 *		this.market_item.file_folder	typeof Array
	 *		this.market_category			typeof MarketCategory model
	 *		this.market_manufacturer		typeof MarketManufacturer model
	 */
	item_plus: function(config) {
		var t=this;
		this.configMarket=config;
		
		switch (config.item_plus.same) {
			case "fid,group":
				var a=this.fields.code.match(/^(.+)--(.*)/);
				if (a) {this.fields.fid=a[1];this.fields.group_id=a[2];} else {this.fields.fid=this.fields.code;this.fields.group_id="";}
				break;
			default:
				this.fields.fid=this.fields.code;
				break;
		}
		this.market_item=this.site.models.MarketItem.Get("get_by_code",{code:this.fields.fid});
		if (this.market_item && this.market_item.fid!=this.fields.fid) {
			switch (config.item_plus.same) {
				case "fid,group":
					this.jsng_response.render_303_redirect("/market/item/"+this.market_item.fid+"--"+(this.market_item.group_id||"")+"/");
					break;
				default:
					this.jsng_response.render_303_redirect("/market/item/"+this.market_item.fid+"/");
				break;
			}
			return;
			//"TODO: Redirect to "+this.market_item.fid;
		}

		if (!this.cart) this.cart=this.F("Cart","get_user_fill");
		this.cart.FetchItemsFull();
		var config1={
			filters:config.item_plus.filters
		};
		config1.filters.fid=this.fields.fid;
		if (config.item_plus.same=="fid,group") config1.filters.group_id=this.fields.group_id;
		this.market_item_pluses=this.F("Market","select_items",config1,config1.filters,config1.filters);
		this.market_item_pluses.forEach(function(mi) {
			mi.cart_amount=t.cart.GetAmountOf({market_item_id:mi.id});
		});
		this.market_item=this.market_item_pluses[0];

		var err;
		if (!this.market_item) {
			err="No item found, searching by fid='"+this.fields.fid+"'";
		} else if (!this.market_item.enabled) {
			err="Item no longer in stock";
		}
		if (err) {
			this.err=err;
			this.http_referer=system.env.HTTP_REFERER;
			this.remote_addr=system.env.REMOTE_ADDR;
			this.http_user_agent=system.env.HTTP_USER_AGENT;
			if (!this.http_user_agent.match(/(Googlebot|YandexBot)/i)) this.F("Functions","send_mail","market_no_item",{},1);
			this.jsng_response.status_code="404";
			return err;
		}
		this.market_category=this.site.models.MarketCategory.Get(this.market_item.category_id);
		this.market_manufacturer=this.site.models.MarketManufacturer.Get(this.market_item.category_id);
		this.market_item.FetchFileFolderFiles();
		if (this.market_item.file_folder_id) this.market_item.file_folder_files = this.site.models.File.List('list_of_file_folder',{file_folder_id:this.market_item.file_folder_id});//GD

		return this.Cview();
	},

	/**
	 * Controller: Market.set_cart_amount
	 *
	 * HTTP args:
	 *		item_id
	 *		amount
	 *		forced_price
	 *
	 * Returns:
	 *		
	 */
	set_cart_amount: function(config) {
		var cart=this.F("Cart","get_user_fill");
		var market_item=this.site.models.MarketItem.Get(this.fields.item_id);
		var is_add=this.fields.amount.match(/^\+(\d+)$/);
		var price;
		if ("Price" in market_item) price=market_item.Price(); else price=market_item.price;
		
		if (this.fields.forced_price) price = this.fields.forced_price;
		
		if (is_add) {
			cart.Add({amount:is_add[1],market_item_id:market_item.id,price:price||0});
		} else if (this.fields.amount.match(/^\d+(|\.\d+)$/)) {
			cart.Set({amount:this.fields.amount,market_item_id:market_item.id,price:price||0});
		} else {
			throw "Не число";
		}
		if (cart.items.length) cart.SaveAll(); else cart.Delete();
		return JSON.stringify({cart:cart.GenerateFullJSON(),call:"CartFuncs.updated"});
	},

	/**
	 * Controller: Market.empty_cart
	 *
	 */
	empty_cart: function(config) {
		var cart=this.F("Cart","get_user_fill");
		cart.Empty();
		cart.SaveAll();
		return JSON.stringify({cart:cart.GenerateFullJSON(),call:"CartFuncs.updated"});
	},

	/**
	 * Controller: Market.fetch_items
	 *
	 */
	fetch_items: function(config) {
		var filters=this.F("Market","gen_filters");
		var items=this.F("Market","select_items",config,{},filters);
//		return JSON.stringify({filters:filters,prepared:this.market_prepared_data});
		return JSON.stringify(items);
	}
},
{
	_type: "functions",
	_section: "Market",

	/**
	 *
	 */
	calc_categories: function(config) {
		this.calc_pages.active_market_categories={};
		var cat;
		if (this.market_category) {
			cat=this.market_category;
		} else if (this.market_item) {
		} else if (this.fields.category_code) {
		} else if (this.fields.item_code) {
		}
		if (!cat) return;
		while (cat) {
			this.calc_pages.active_market_categories[cat.id]=1;
			cat=this.site.models.MarketCategory.Get(cat.parent_id);
		}
	},

	/**
	 * Function: Market.select_items
	 *
	 * Parameters:
	 *		config typeof Object
	 *		config.limit
	 *		config.offset
	 */	
	select_items: function(config,conf,filter) {
//		throw {config:config,conf:confifilter:filter};
		/*if (!this.market_prepared_data) */ this.market_prepared_data=this.F("Market","prepare_select_items",config,conf,filter);
		//throw this.market_prepared_data;


//		if (config.filters && config.filters.partition_by && config.filters.partition_by.match(/^\w+$/)) plus("	main.id in (select min(id) from Tmarket_items group by "+config.filters.partition_by+")");

		var sqlq=this.site.sql.load("_models/market_items/get");
		sqlq=sqlq.replace(/\nwhere/,this.market_prepared_data.from+"\nwhere");
		sqlq=sqlq.replace(/	main.id=:id/,this.market_prepared_data.where);
		
		var cat_tid=sqlq.match(/Tmarket_categories_l10n t(\w+)l1/)[1];
		var man_tid=sqlq.match(/Tmarket_manufacturers_l10n t(\w+)l1/)[1];

		if (this.market_prepared_data.order_by) sqlq+="\norder by\n	"+this.market_prepared_data.order_by;

		if (config.filters && config.filters.partition_by && config.filters.partition_by.match(/^[\w,\.]+$/)) {
			sqlq=sqlq.replace(/\nfrom/,",\n	row_number() over ( partition by "+config.filters.partition_by+" order by "+(this.market_prepared_data.order_by||"main.ordering")+" ) as rn\nfrom");
			sqlq="select * from (\n"+sqlq+"\n) tmp\nwhere tmp.rn=1\n";
		}
		

		if ("limit" in conf) {sqlq+="\nlimit :limit";this.market_prepared_data.bind.limit=conf.limit;}
		if ("offset" in conf) {sqlq+="\noffset :offset";this.market_prepared_data.bind.offset=conf.offset;}

		sqlq=sqlq.replace(/t_man_l/g,"t"+man_tid+"l").replace(/t_cat_l/g,"t"+cat_tid+"l");
		//throw sqlq;
		
		//if (this.fields.search_all) throw sqlq;
		
		//throw {bind:this.market_prepared_data.bind,sqlq:sqlq};
		//throw sqlq;
		//throw this.market_prepared_data.bind;
		var arr=this.site.sql.execute_and_fetch_single(sqlq,this.market_prepared_data.bind);
		this.market_items_generated_sql=sqlq;
//		if (config.limit) throw {bind:this.market_prepared_data.bind,sqlq:sqlq};
		return this.site.models.MarketItem.FromArray(arr);
	},

	count_items: function(config,conf,filter) {
		if (!this.market_prepared_data) this.market_prepared_data=this.F("Market","prepare_select_items",config,conf,filter);
		var sqlq=this.site.sql.load("_models/market_items/count");
		sqlq+=this.market_prepared_data.from;
		sqlq+="\nwhere\n";
		sqlq+=this.market_prepared_data.where;

		if (config.filters && config.filters.partition_by && config.filters.partition_by.match(/^[\w,\.]+$/)) {
			sqlq=sqlq.replace(/count\(\*\) as cnt/,"row_number() over ( partition by "+config.filters.partition_by+" order by main.ordering ) as rn");
			sqlq="select count(*) as cnt from (\n"+sqlq+"\n) tmp\nwhere tmp.rn=1\n";
		}

		var arr=this.site.sql.execute_and_fetch_single(sqlq,this.market_prepared_data.bind);
		return arr[0].cnt;
	},

	/**
	 *
	 */
	gen_conf: function(config) {
		var conf={};
		if (config.filters && config.filters.limit) {
			if (this.fields.page!="all") {
				conf.limit=config.filters.limit;
				conf.offset=conf.limit*(this.fields.page||0);
			}
		}
//		throw {conf:conf,config:config,fields:this.fields};
		return conf;
	},

	/**
	 *
	 */
	gen_filters: function() {
		var t=this;
		var f={enums:{},ranges:{}};
		function gen_range(src,dst1,dst2)
		{
			var a=t.fields[src].match(/^(.*)\.\.(.*)$/);
			dst1[dst2]={min:a[1],max:a[2]};
		}
		function gen_enum(src,dst1,dst2)
		{
			dst1[dst2]=t.fields[src].replace(/[^\d,]+/g,"").split(/,/);
		}

		for (var k in t.fields) {
			if (t.fields[k]=="") continue;
			var a=k.match(/^(\w+)-(\d+)$/);
			switch(a?a[1]:k) {
				case "name_or_fid":
					/*var tmp=t.site.sql.execute_and_fetch_single("select 1 from Tmarket_items where fid=:test limit 1",{test:t.fields[k]});
					if (tmp.length) f.fid=t.fields[k]; else f.name_like=t.fields[k];
					break;*/
				case "category_id":
				case "group_id":
				case "name_like":
				case "search_all":
				case "fid":
				case "fid2":
				case "sort":
					f[k]=t.fields[k];
					break;
				case "price":
					gen_range("price",f,"price");
					break;
				case "range":
					gen_range(k,f.ranges,a[2]);
					break;
				case "enum":
					gen_enum(k,f.enums,a[2]);
					break;
				case "manufacturers":
					gen_enum(k,f,"manufacturers");
					break;
			}
		}
		return f;
	},

	/**
	 * Function: Market.prepare_select_items
	 *
	 * Parameters:
	 *		config typeof Object								- Market configuration
	 *		config.prepare_select_items_helpers typeof Array	-

	 *		conf typeof Object									- configuration
	 *		filters typeof Object
	 *			filters.category_id		typeof Integer
	 *			filters.group_id		typeof Integer
	 *			filters.manufacturers	typeof Array of Integer
	 *			filters.sort			typeof String
	 *			filters.name_like		typeof String
	 *			filters.name_or_fid		typeof String
	 *			filters.price			typeof Object {min,max}
	 *			filters.enums[Number]	typeof Array of Integer
	 *			filters.menums[Number]	typeof Array of Integer
	 *			filters.ranges[Number]	typeof Object {min,max}
	 *			filters.has_old_price	typeof Integer 0/1
	 *			filters.is_new			typeof Integer 0/1/undefined
	 *			filters.is_featured		typeof Integer 0/1/undefined
	 *			filters.is_recomended	typeof Integer 0/1/undefined
	 *			config.filters.in_stock		typeof Integer 0/1/undefined
	 *			config.filters.partition_by	typeof String
	 *	
	 */
	prepare_select_items: function(config,conf,filters) {
		var t=this;
		var h={
			where:"	main.enabled=1",
			order_by:"",
			from:"",
			bind: {}
		};
		var tables={};
		function plus(a,or)
		{
			h.where+=or?" or\n":" and\n";
			h.where+=a;
		}
		function plus_table(table,tprefix,has_l10n)
		{
			if (tables[table]) return;
			tables[table]=1;
			h.from+="\n	left join "+table+" t"+tprefix+" on main.id=t"+tprefix+".item_id";
		}
		var sort;
		if (config.filters) sort=config.filters.sort;
		if (!sort) sort=filters.sort;
		if (!sort && config.filters) sort=config.filters.softly_sort;
		if (!sort) sort="";
		
		//throw filters;
		switch(sort) {
			case "name":
				h.order_by="coalesce(mainl1.name,mainl2.name)";
				break;
			case "fid":
				h.order_by="main.fid";
				break;
			case "fid2":
				h.order_by="main.fid2";
				break;
			case "manufacturer":
				h.order_by="coalesce(t_man_l1.name,t_man_l2.name), coalesce(mainl1.name,mainl2.name)";
				break;
			case "category":
				h.order_by="coalesce(t_cat_l1.name,t_cat_l2.name), coalesce(mainl1.name,mainl2.name)";
				break;
			case "category_ordering":
				h.order_by="t5.ordering, coalesce(mainl1.name,mainl2.name)";
				break;
			case "price":
				h.order_by="main.price nulls last,coalesce(mainl1.name,mainl2.name)";
				break;
			case "random":
				h.order_by="random()";
				break;
			case "ordering":
			case "":
				h.order_by="main.ordering";
				break;
			case "sql":
				h.order_by=config.filters.sort_sql;
				break;
		}
		["group_id","category_id","is_new","is_featured","is_recomended"].forEach(function(a) {
			if (a in filters) {plus("	main."+a+"=:"+a);h.bind[a]=filters[a];}
		});
		if (filters.manufacturers) plus("	main.manufacturer_id in ("+((typeof filters.manufacturers=="number")?filters.manufacturers:filters.manufacturers.join(",").replace(/[^\d\,]/g,""))+")");
		if (filters.name_like) {plus("	lower(coalesce(mainl1.name,mainl2.name)) like '%'||:name||'%'");h.bind.name=filters.name_like.toLowerCase();}
		
		if (filters.search_all) {
			plus("	(lower(coalesce(mainl1.name,mainl2.name)) like '%'||:search_string||'%'");
			plus("	lower(coalesce(mainl1.abstract,mainl2.abstract)) like '%'||:search_string||'%'",true);
			plus("	lower(coalesce(mainl1.body,mainl2.body)) like '%'||:search_string||'%'",true);
			plus("	lower(coalesce(mainl1.body2,mainl2.body2)) like '%'||:search_string||'%')",true);			
			h.bind.search_string=filters.search_all.toLowerCase();
		}
		
		if (filters.name_or_fid) {plus("	(lower(coalesce(mainl1.name,mainl2.name)) like '%'||:name_or_fid||'%' or lower(main.fid) like '%'||:name_or_fid||'%')");h.bind.name_or_fid=filters.name_or_fid.toLowerCase();}
		if (filters.fid) {plus("	lower(main.fid)=:fid");h.bind.fid=filters.fid.toLowerCase();}
		if (filters.fid2) {plus("	lower(main.fid2)=:fid2");h.bind.fid2=filters.fid2.toLowerCase();}
		if (filters.price) {plus("	main.price>=:min_price and main.price<=:max_price");h.bind.min_price=filters.price.min;h.bind.max_price=filters.price.max;}
		if (filters.has_old_price || (config.filters && config.filters.has_old_price)) {plus("	main.old_price is not null");}
		if (config.filters && ("in_stock" in config.filters)) plus(config.filters.in_stock?"	main.in_stock_count>0":"main.in_stock_count=0");
//		if (config.filters && config.filters.partition_by && config.filters.partition_by.match(/^\w+$/)) plus("	main.id in (select min(id) from Tmarket_items group by "+config.filters.partition_by+")");
		for (var k in filters.enums) {
			var fld=this.site.sql.execute_and_fetch_one("market_fields/get",{field_id:k});
			switch (fld.datatype_code) {
				case "lenums":
					throw "TODO";
				case "enums":
					plus("exists (select 1 from Tmarket_item_fields_enums mife where mife.item_id=main.id and mife.enum_id in ("+filters.enums[k].join(",")+"))");
					break;
				default:
					var table="Tmarket_item_fields_"+fld.datatype_prefix;
					var col=(fld.group_category_id?"fldcat":"fldany")+fld.cid;
					plus_table(table,fld.datatype_prefix);
					plus(	"t"+fld.datatype_prefix+"."+col+" in ("+filters.enums[k].join(",")+")");
					break;
			}
		}
		for (var k in filters.ranges) {
			var fld=this.site.sql.execute_and_fetch_one("market_fields/get",{field_id:k});
			var table="Tmarket_item_fields_"+fld.datatype_prefix;
			var col=(fld.group_category_id?"fldcat":"fldany")+fld.cid;
			plus_table(table,fld.datatype_prefix);
			plus("	t"+fld.datatype_prefix+"."+col+">=:"+col+"_min and t"+fld.datatype_prefix+"."+col+"<=:"+col+"_max");
			h.bind[col+"_min"]=filters.ranges[k].min;
			h.bind[col+"_max"]=filters.ranges[k].max;
		}
		if (config.prepare_select_items_helpers) {
			config.prepare_select_items_helpers.forEach(function(fh) {
				var arr=fh.match(/^(\w+)\.(\w+)$/);
				var r=t.F(arr[1],arr[2],{h:h,filters:filters});
				if (r) throw r;
			});
		}
		//if (filters.search_all) throw h;
		return h;
	},

	/**
	 *
	 */
	gen_same_filters_url: function(conf,filters) {
		var href="";
		function add_atom(k,v) {
			if (v===undefined || v===null) return;
			if (v==="") return;
			href+="&"+k+"="+v;
		}
		function add_range(k,v) {
			if (v===undefined || v===null) return;
			href+="&"+k+"="+v.min+".."+v.max;
		}
		function add_enum(k,v) {
			if (v===undefined || v===null) return;
			href+="&"+k+"="+v.join(",");
		}
		for (var k in filters) {
			switch(k) {
				case "price":
					add_range(k,filters[k]);
					break;
				case "manufacturers":
					add_enum(k,filters[k]);
					break;
				case "enums":
					for (var k2 in filters[k]) add_enum("enum-"+k2,filters[k][k2]);
					break;
				case "emenums":
					break;
				case "ranges":
					for (var k2 in filters[k]) add_range("range-"+k2,filters[k][k2]);
					break;
				case "name_or_fid":
					break;
				default:
					add_atom(k,filters[k]);
					break;
			}
		}
		add_atom("page",conf.page);
//		if (config.page) href+="&page="+config.page;
		href=href.replace(/^&/,"?");
		return href;
	}

}];

